﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

using KSOAdmin.Core.EFDbContext;
using KSOAdmin.Core.Expressions;
using KSOAdmin.IRepository;
using KSOAdmin.Models.Base;
using KSOAdmin.Models.Core;
using KSOAdmin.Models.MapperModels.Enums;
using KSOAdmin.Models.MapperModels.Page;

using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;

using StackExchange.Redis;

namespace KSOAdmin.Repository
{
    /// <summary>
    /// 父类的实现
    /// </summary>
    /// <typeparam name="TModel"></typeparam>
    public class BasicRepository<TModel> : IDependency, IBasicRepository<TModel> where TModel : class, new()
    {
        private KSOContext DefaultDbContext { get; set; }

        public BasicRepository(KSOContext _DbContext)
        {
            DefaultDbContext = _DbContext ?? throw new Exception("请实例化DbContext!");
        }

        public virtual KSOContext DbContext
        {
            get { return DefaultDbContext; }
        }

        private DbSet<TModel> DBSet
        {
            get { return DefaultDbContext.Set<TModel>(); }
        }

        /// <summary>
        /// 事务
        /// </summary>
        /// <param name="responses"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<ResponseModel> DbContextTransaction(Func<ResponseModel> responses)
        {
            ResponseModel respon = new ResponseModel();
            using (var tran = await DefaultDbContext.Database.BeginTransactionAsync())
            {
                try
                {
                    respon = responses();
                    if (respon.isSuccess)
                    {
                        await tran.CommitAsync();
                        respon.code = 200;
                    }
                    else
                    {
                        await tran.RollbackAsync();
                        respon.code = -1;
                    }
                    return respon;
                }
                catch (Exception ex)
                {
                    await tran.RollbackAsync();
                    throw new Exception(ex.Message);
                }
            }
        }

        #region 添加
        public int Add(TModel entities, bool SaveChanges = true)
        {
            try
            {
                return AddRange(new List<TModel>() { entities }, SaveChanges);
            }
            catch (Exception)
            {

                throw;
            }

        }
        public int AddRange(IEnumerable<TModel> entities, bool SaveChanges = true)
        {
            DBSet.AddRange(entities);
            if (SaveChanges) return DbContext.SaveChanges();
            return 0;
        }
        public async Task<int> AddAsync(TModel entities, bool SaveChanges = true)
        {
            await DBSet.AddRangeAsync(entities);
            if (SaveChanges) return await DbContext.SaveChangesAsync();
            return 0;
        }

        public async Task<int> AddAsync<T>(T entities, bool SaveChanges = true) where T : class
        {
            await DefaultDbContext.Set<T>().AddRangeAsync(entities);
            if (SaveChanges) return await DbContext.SaveChangesAsync();
            return 0;
        }

        public async Task<int> AddRangeAsync(IEnumerable<TModel> entities, bool SaveChanges = true)
        {
            await DBSet.AddRangeAsync(entities);
            if (SaveChanges) return await DbContext.SaveChangesAsync();
            return 0;
        }
        #endregion


        #region 删除

        public int Delete(TModel model, bool saveChanges = true)
        {
            DBSet.Remove(model);
            if (saveChanges) return DefaultDbContext.SaveChanges();
            return 0;
        }
        public async Task<int> DeleteAsync(TModel model, bool saveChanges = true)
        {
            DefaultDbContext.Entry<TModel>(model).State = EntityState.Deleted;
            if (saveChanges) return await DefaultDbContext.SaveChangesAsync();
            return 0;
        }
        /// <summary>
        /// 批量删除自己
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public int DeleteRange(Expression<Func<TModel, bool>> expression, bool saveChanges = true)
        {
            Type entityType = typeof(TModel);
            List<TModel> models = DBSet.Where(expression).ToList();
            DefaultDbContext.RemoveRange(models);
            if (saveChanges) return DefaultDbContext.SaveChanges();
            return 0;
        }
        /// <summary>
        /// 批量删除自己
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public async Task<int> DeleteRangeAsync(Expression<Func<TModel, bool>> expression, bool saveChanges = true)
        {
            Type entityType = typeof(TModel);
            List<TModel> models = await DBSet.Where(expression).ToListAsync();
            foreach (var item in models)
            {
                DefaultDbContext.Entry<TModel>(item).State = EntityState.Deleted;
            }
            if (saveChanges) return await DefaultDbContext.SaveChangesAsync();
            return 0;
        }
        /// <summary>
        /// 批量删除自己
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public async Task<int> DeleteRangeAsync(List<TModel> list, bool saveChanges = true)
        {
            try
            {
                DefaultDbContext.RemoveRange(list);
                if (saveChanges) return await DefaultDbContext.SaveChangesAsync();
                return 0;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        /// <summary>
        /// 主从删除 借鉴 VOL
        /// </summary>
        /// <param name="keys"></param>
        /// <param name="delList"></param>
        /// <returns></returns>
        public int DeleteWithKeys(object[] keys, bool delList = true)
        {
            Type entityType = typeof(TModel);
            string tKey = entityType.GetEntityTableKey();
            string joinKeys = $"'{string.Join("','", keys)}'";
            string sql = $"DELETE FROM {entityType.Name } where {tKey} in ({joinKeys});";
            if (delList)
            {
                Type detailType = entityType.GetCustomAttribute<EntityAttribute>().DetailTable?[0];
                if (detailType != null)
                    sql = sql + $"DELETE FROM {detailType.GetEntityTableName()} where {tKey} in ({joinKeys});";
            }
            return ExecuteSqlCommand(sql);
        }

        #endregion

        #region 查询

        /// <summary>
        /// 获取第一个
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public TModel FindFirst(Expression<Func<TModel, bool>> expression)
        {
            if (expression is null) expression = pre => true;
            return DBSet.AsNoTracking().Where(expression).FirstOrDefault();
        }
        /// <summary>
        /// 异步获取第一个
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<TModel> FindFirstAsync(Expression<Func<TModel, bool>> expression) 
        {
            if (expression is null) expression = pre => true;
            return await DBSet.Where(expression).FirstOrDefaultAsync();
        }

        public async Task<List<TModel>> FindListAsync(Expression<Func<TModel, bool>> expression, Expression<Func<TModel, Dictionary<object, QueryOrderBy>>> orderBy = null, bool isTracking = true)
        {
            return await this.FindAsIQueryable(expression, orderBy, isTracking).ToListAsync();
        }

        public IQueryable<TModel> FindAsIQueryable(Expression<Func<TModel, bool>> predicate, Expression<Func<TModel, Dictionary<object, QueryOrderBy>>> orderBy = null, bool isTracking = true)
        {
            IQueryable<TModel> query = DefaultDbContext.Set<TModel>();
            if (predicate is null) predicate = pre => true;
            if (isTracking)
            {
                return query.Where(predicate).GetIQueryableOrderBy(orderBy.GetExpressionToDic()).AsQueryable<TModel>();
            }
            else
            {
                return query.AsNoTracking().Where(predicate).GetIQueryableOrderBy(orderBy.GetExpressionToDic());
            }
        }
        /// <summary>
        /// 自定义泛型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="predicate"></param>
        /// <param name="isTracking"></param>
        /// <returns></returns>
        public IQueryable<T> FindOtherIQueryable<T>(Expression<Func<T, bool>> predicate, Expression<Func<T, Dictionary<object, QueryOrderBy>>> orderBy = null, bool isTracking = true) where T : class
        {
            if (predicate is null) predicate = pre => true;
            IQueryable<T> query = DefaultDbContext.Set<T>();
            if (isTracking)
            {
                return query.Where(predicate).GetIQueryableOrderBy(orderBy.GetExpressionToDic()).AsQueryable();
            }
            else
            {
                return query.AsNoTracking().Where(predicate).GetIQueryableOrderBy(orderBy.GetExpressionToDic());
            }
        }
        /// <summary>
        /// 自定义泛型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="predicate"></param>
        /// <param name="orderBy"></param>
        /// <param name="isTracking"></param>
        /// <returns></returns>
        public async Task<List<T>> FindOtherListAsync<T>(Expression<Func<T, bool>> predicate, bool isTracking = true) where T : class
        {
            if (predicate is null) predicate = pre => true;
            return await this.FindOtherIQueryable(predicate, null, isTracking).ToListAsync();
        }
        #endregion

        #region sql

        public virtual int ExecuteSqlCommand(string sql, params SqlParameter[] sqlParameters)
        {
            return DbContext.Database.ExecuteSqlRaw(sql, sqlParameters);
        }

        public virtual List<TModel> FromSql(string sql, params SqlParameter[] sqlParameters)
        {
            return DBSet.FromSqlRaw(sql, sqlParameters).ToList();
        }
        /// <summary>
        /// 执行sql
        /// 使用方式 FormattableString sql=$"select * from xx where name ={xx} and pwd={xx1} "，
        /// FromSqlInterpolated内部处理sql注入的问题，直接在{xx}写对应的值即可
        /// 注意：sql必须 select * 返回所有TEntity字段，
        /// </summary>
        /// <param name="formattableString"></param>
        /// <returns></returns>
        public virtual IQueryable<TModel> FromSqlInterpolated([NotNull] FormattableString sql)
        {
            return DBSet.FromSqlInterpolated(sql);
        }

        #endregion

        #region 分页
        public IQueryable<T> IQueryablePage<T>(out int rowcount, Expression<Func<T, bool>> predicate, Expression<Func<T, Dictionary<object, QueryOrderBy>>> orderBy = null, int pageIndex = 1, int pageSize = 20) where T : class
        {
            pageIndex = pageIndex <= 0 ? 1 : pageIndex;
            pageSize = pageSize <= 0 ? 10 : pageSize;
            if (predicate is null) predicate = pre => true;

            var _db = DbContext.Set<T>();
            rowcount = _db.Count(predicate);
            return DbContext.Set<T>().Where(predicate)
                .GetIQueryableOrderBy(orderBy.GetExpressionToDic())
                .Skip((pageIndex - 1) * pageSize)
                .Take(pageSize);
        }

        public IQueryable<TModel> IQueryablePage(out int rowcount, Expression<Func<TModel, bool>> predicate, Expression<Func<TModel, Dictionary<object, QueryOrderBy>>> orderBy = null, int pageIndex = 1, int pageSize = 20)
        {
            return this.IQueryablePage<TModel>(out rowcount, predicate, orderBy, pageIndex, pageSize);
        }

        public async Task<PageListModel<TModel>> PageListAsync(Expression<Func<TModel, bool>> predicate, Expression<Func<TModel, Dictionary<object, QueryOrderBy>>> orderBy = null, int pageIndex = 1, int pageSize = 20)
        {
            PageListModel<TModel> pageListModel = await PageListAsync(DBSet.Where(predicate), orderBy, pageIndex, pageSize);
            return pageListModel;
        }
        public async Task<PageListModel<TModel>> PageListAsync(IQueryable<TModel> queryable, Expression<Func<TModel, Dictionary<object, QueryOrderBy>>> orderBy = null, int pageIndex = 1, int pageSize = 20)
        {
            return await PageOtherListAsync<TModel>(queryable, orderBy, pageIndex, pageSize);
        }
        public async Task<PageListModel<T>> PageOtherListAsync<T>(IQueryable<T> queryable, Expression<Func<T, Dictionary<object, QueryOrderBy>>> orderBy = null, int pageIndex = 1, int pageSize = 20) where T : class
        {
            PageListModel<T> pageListModel = new PageListModel<T>();
            pageListModel.total = queryable.Count();
            pageListModel.listPage = await queryable.GetIQueryableOrderBy(orderBy.GetExpressionToDic())
                .Skip((pageIndex - 1) * pageSize)
                .Take(pageSize).ToListAsync();
            pageListModel.pageCount = pageListModel.total / pageSize;
            pageListModel.pageSize = pageSize;
            pageListModel.pageIndex = pageIndex;
            return pageListModel;
        }

        #endregion

        #region 修改
        public int Update(TModel entity, bool saveChanges = true)
        {
            DBSet.Update(entity);
            if (saveChanges) return DefaultDbContext.SaveChanges();
            return 0;
        }

        public async Task<int> UpdateAsync(TModel entity, bool saveChanges = true)
        {
            DefaultDbContext.Entry<TModel>(entity).State = EntityState.Modified;
            if (saveChanges) return await DefaultDbContext.SaveChangesAsync();
            return 0;
        }

        public int Update<T>(T entity, bool saveChanges = true) where T : class
        {
            DefaultDbContext.Set<T>().Update(entity);
            if (saveChanges) return DefaultDbContext.SaveChanges();
            return 0;
        }

        public async Task<int> UpdateAsync<T>(T entity, bool saveChanges = true) where T : class
        {
            DefaultDbContext.Entry<T>(entity).State = EntityState.Modified;
            if (saveChanges) return await DefaultDbContext.SaveChangesAsync();
            return 0;
        }
        public int UpdateRange<T>(IEnumerable<T> entities, bool saveChanges = true) where T : class
        {
            foreach (var item in entities)
            {
                DefaultDbContext.Entry<T>(item).State = EntityState.Modified;
            }
            if (saveChanges) return DefaultDbContext.SaveChanges();
            return 0;
        }
        public async Task<int> UpdateRangeAsync(IEnumerable<TModel> entities, bool saveChanges = true)
        {
            foreach (var item in entities)
            {
                DefaultDbContext.Entry<TModel>(item).State = EntityState.Modified;
            }
            if (saveChanges) return await DefaultDbContext.SaveChangesAsync();
            return 0;
        }

        #endregion

        /// <summary>
        /// 自定义的保存
        /// </summary>
        /// <returns></returns>
        public virtual int SaveChanges()
        {
            return DbContext.SaveChanges();
        }

        public virtual Task<int> SaveChangesAsync()
        {
            return DbContext.SaveChangesAsync();
        }

       
    }
}
